package com.mg.plugin.agrometeorology.model;

import com.mg.model.Model;
import com.mg.model.Param;
import com.mg.model.Port;
import com.mg.plugin.agrometeorology.common.StationDataType;
import com.mg.plugin.agrometeorology.entity.StationData;
import com.mg.pluginexample2.ModuleBase;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 稻瘟病
 */
public class RiceBlastModel extends ModuleBase {
    public RiceBlastModel(int nID) {
        super(nID);
        m_alInputPort.add(new Port(this, Port.Type.Unknown));
        m_alInputPort.add(new Port(this, Port.Type.Unknown));
        m_alOutputPort.add(new Port(this, Port.Type.Unknown));
    }

    public String GetGroupName() {
        return "灾害制作";
    }

    public String GetName() {
        return "稻瘟病";
    }

    public String GetDescription() {
        return "吉林灾害制作：稻瘟病6月至8月发病虫害发生等级预报";
    }

    public boolean OnAttach(Port portFrom, Port portTo) {
        try {
            int i = FindPort(portFrom, false);
            if (i != -1) {
                return true;
            }
            i = FindPort(portTo, true);
            if (i == -1) {
                return false;
            }

            ArrayList<Param> alParam = new ArrayList<Param>();
            portFrom.GetModule().GetOutputParam(portFrom, alParam);

            if (alParam.size() != 1) {
                return false;
            }

            if (alParam.get(0).m_objValue != null && !(alParam.get(0).m_objValue instanceof ArrayList)) {
                return false;
            }
            if (i == 0) {
                m_inputForecastList = (List<String>) alParam.get(0).m_objValue;
            }
            if (i == 1) {
                m_inputRealityList = (List<String>) alParam.get(0).m_objValue;
            }

            portTo.SetType(portFrom.GetType());
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
        return true;
    }

    public boolean OnDetach(Port portFrom, Port portTo) {
        if (!super.OnDetach(portFrom, portTo))
            return false;
        try {
            int i = FindPort(portTo, true);
            if (i == -1)
                return true;
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    //用于 参数->XML 等
    public int GetParam(ArrayList<Param> alParam) {
        try {
            super.GetParam(alParam);

            alParam.add(new Param("StationId", m_strStationId, "站点ID对应字段", "StationId", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("Date", m_strDate, "时间对应字段", "Date", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("Temperature", m_strTemp, "温度对应字段", "Temperature", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("MinTemperature", m_strMinTemp, "最低温度对应字段", "MinTemperature", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("Precipitation", m_strRain, "降水对应字段", "Precipitation", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("Humidity", m_strHumidity, "湿度对应字段", "Humidity", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("Sunshine", m_strSunshine, "日照对应字段", "Sunshine", "参数匹配", Param.EditType.Default));
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return alParam.size();
    }

    public boolean OnParamChanged(ArrayList<Param> alParam, int nIndex, Object objValue) {
        return super.OnParamChanged(alParam, nIndex, objValue);
    }

    //用于 XML->参数 等
    public int SetParam(final ArrayList<Param> alParam) {
        int i = super.SetParam(alParam);
        try {
            m_strStationId = (String) alParam.get(i++).m_objValue;
            m_strDate = (String) alParam.get(i++).m_objValue;
            m_strTemp = (String) alParam.get(i++).m_objValue;
            m_strMinTemp = (String) alParam.get(i++).m_objValue;
            m_strRain = (String) alParam.get(i++).m_objValue;
            m_strHumidity = (String) alParam.get(i++).m_objValue;
            m_strSunshine = (String) alParam.get(i++).m_objValue;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return i;
    }

    public int GetOutputParam(Port port, ArrayList<Param> alParam) {
        int i = FindPort(port, false);
        if (i == -1) {
            System.out.println("Post Is Null");
            return 0;
        }

        alParam.add(new Param("ResultString", m_listCSV));
        return alParam.size();
    }

    @SuppressWarnings("static-access")
    public boolean Execute() {
        m_listCSV.clear();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String head = "StationNum,Data,Time";
        String type = "String,Double,String";
        m_listCSV.add(head);
        m_listCSV.add(type);
        Map<String, List<StationData>> stationDataList = new HashMap<String, List<StationData>>();
        if (m_inputRealityList != null && m_inputRealityList.size() > 2) {
            String realHeader = m_inputRealityList.get(0);

            if (realHeader != null && !verifyHeader(realHeader)) {
                m_model.OutputLog(Model.LogLevel.Error, "Header Can Not Match!");
                return false;
            } else {
                int realStationIndex = findDataIndex(realHeader, m_strStationId);
                int realDateIndex = findDataIndex(realHeader, m_strDate);
                int realTempIndex = findDataIndex(realHeader, m_strTemp);
                int realMinTempIndex = findDataIndex(realHeader, m_strMinTemp);
                int realRainIndex = findDataIndex(realHeader, m_strRain);
                int realHumidityIndex = findDataIndex(realHeader, m_strHumidity);
                int realSunshineIndex = findDataIndex(realHeader, m_strSunshine);

                formatStationData(m_inputRealityList, realStationIndex, realDateIndex, realTempIndex,
                        realMinTempIndex, realRainIndex, realHumidityIndex, realSunshineIndex, stationDataList);
            }
        }
        if (m_inputForecastList != null && m_inputForecastList.size() > 2) {
            String foreHeader = m_inputForecastList.get(0);
            if (foreHeader != null && !verifyHeader(foreHeader)) {
                m_model.OutputLog(Model.LogLevel.Error, "Header Can Not Match!");
                return false;
            } else {
                int foreStationIndex = findDataIndex(foreHeader, m_strStationId);
                int foreDateIndex = findDataIndex(foreHeader, m_strDate);
                int foreTempIndex = findDataIndex(foreHeader, m_strTemp);
                int foreMinTempIndex = findDataIndex(foreHeader, m_strMinTemp);
                int foreRainIndex = findDataIndex(foreHeader, m_strRain);
                int foreHumidityIndex = findDataIndex(foreHeader, m_strHumidity);
                int foreSunshineIndex = findDataIndex(foreHeader, m_strSunshine);

                formatStationData(m_inputForecastList, foreStationIndex, foreDateIndex, foreTempIndex,
                        foreMinTempIndex, foreRainIndex, foreHumidityIndex, foreSunshineIndex, stationDataList);

            }
        }


        try {
            for (String stationId : stationDataList.keySet()) {
                float g = calcDisasterLevel(stationDataList.get(stationId));
                if (!Float.isNaN(g) && !Float.isInfinite(g)) {
                    m_listCSV.add(stationId + "," + g + "," + sdf.format(new Date()));
                }

            }
        } catch (ParseException e) {
            m_model.OutputLog(Model.LogLevel.Error, "ParseException!");
            e.printStackTrace();
            return false;
        }
        OnParamChanged();
        return true;
    }

    /**
     * String数据转换为对象数据
     *
     * @param inputRealityList
     * @param realStationIndex
     * @param realDateIndex
     * @param realTempIndex
     * @param realMinTempIndex
     * @param realRainIndex
     * @param realHumidityIndex
     * @param realSunshineIndex
     * @param stationDataList
     */
    private void formatStationData(List<String> inputRealityList, int realStationIndex, int realDateIndex, int realTempIndex, int realMinTempIndex, int realRainIndex, int realHumidityIndex, int realSunshineIndex, Map<String, List<StationData>> stationDataList) {
        for (int i = 2; i < inputRealityList.size(); i++) {
            String stationId = inputRealityList.get(i).split(",")[realStationIndex];
            StationData stationData = new StationData(inputRealityList.get(i), realStationIndex, realDateIndex, realTempIndex, realMinTempIndex, realRainIndex, realHumidityIndex, realSunshineIndex, StationDataType.RICE_BLAST);

            if (stationDataList.keySet().contains(stationId)) {
                stationDataList.get(stationId).add(stationData);
            } else {
                List<StationData> dataList = new ArrayList<StationData>();
                dataList.add(stationData);
                stationDataList.put(stationId, dataList);
            }
        }
    }

    /**
     * 表头检验
     *
     * @param realHeader 实况数据表头
     * @return
     */
    private boolean verifyHeader(String realHeader) {

        if (realHeader.contains(m_strDate)) {
            if (realHeader.contains(m_strTemp) && realHeader.contains(m_strMinTemp)
                    && realHeader.contains(m_strRain) && realHeader.contains(m_strHumidity)
                    && realHeader.contains(m_strSunshine)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 灾害等级计算
     *
     * @param stationDataList
     * @return
     * @throws ParseException
     */
    private float calcDisasterLevel(List<StationData> stationDataList) throws ParseException {
        List<StationData> stationDatas = sortStationData(stationDataList);
        //count：适宜致病日中日最低气温小于20℃的累积日数
        int count = 0;
        int tmp = 0;
        //dataRes 适宜致病日为n天的集合
        List<Integer> dataRes = new ArrayList<Integer>();
        for (StationData data : stationDatas) {
            if(data.getMinTemp()<20){
                count++;
            }
            if (isFIt(data)) {
//                count++;
                tmp++;
                m_model.OutputLog(Model.LogLevel.Info, "calcDisasterLevel:"
                        + data.getStationId() + ":" + data.getDate() + tmp + ":" + data.getHumidity());
            } else {
                dataRes.add(tmp);
                tmp = 0;
            }
        }
        dataRes.add(tmp);

        float total = count * 0.1f;
        for (Integer n : dataRes) {
            total += (n + 1) * n / 2;
        }
        return total;
    }

    private List<StationData> sortStationData(List<StationData> stationDataList) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        StationData[] tempList = new StationData[stationDataList.size()];
        for (int i = 0; i < stationDataList.size(); i++) {
            tempList[i] = stationDataList.get(i);
        }
        StationData tmp;
        for (int i = 0; i < tempList.length; i++) {
            for (int j = i; j < tempList.length; j++) {
                Date dateI = sdf.parse(tempList[i].getDate());
                Date dateJ = sdf.parse(tempList[j].getDate());
                if (dateI.after(dateJ)) {
                    tmp = tempList[i];
                    tempList[i] = tempList[j];
                    tempList[j] = tmp;
                }
            }
        }
        List<StationData> resDataList = new ArrayList<StationData>();
        for (StationData tmps : tempList) {
            resDataList.add(tmps);
        }
        return resDataList;
    }

    private float calcWTemp(List<String> dataList) {
        float total = 0;
        for (String tmp : dataList) {
            total += Float.parseFloat(tmp);
        }

        return total / dataList.size();
    }

    private float calcRain(List<String> dataList) {
        float total = 0;
        for (String tmp : dataList) {
            if (tmp.split(",").length == 2) {
                total += Float.parseFloat(tmp.split(",")[0]);
            } else {
                total += Float.parseFloat(tmp);
            }
        }

        return total;
    }

    private float calcAvgTemp(List<String> dataList) {
        float total = 0;
        for (String tmp : dataList) {
            if (tmp.split(",").length == 2) {
                total += Float.parseFloat(tmp.split(",")[1]);
            } else {
                total += Float.parseFloat(tmp);
            }
        }
        return total / dataList.size();
    }


    private boolean isFIt(StationData data) {
        if (data.getTemp() >= 20 && data.getTemp() <= 30
                && data.getMinTemp() < 20
                && data.getHumidity() >= 0.9
                && data.getSunshine() <= 5
                && data.getRain() >= 0.1) {
            return true;
        }
        return false;
    }

    private int findDataIndex(String realHeader, String strRain) {

        String[] data = realHeader.split(",");
        for (int i = 0; i < data.length; i++) {
            if (data[i].equalsIgnoreCase(strRain)) {
                return i;
            }
        }
        return -1;
    }

    private ArrayList<Param> m_alParam = new ArrayList<Param>();

    private List<String> m_inputForecastList = new ArrayList<String>();
    private List<String> m_inputRealityList = new ArrayList<String>();
    private List<String> m_listCSV = new ArrayList<String>();

    private String m_strStationId;
    private String m_strDate;
    private String m_strTemp;
    private String m_strMinTemp;
    private String m_strRain;
    private String m_strHumidity;
    private String m_strSunshine;
}

